from flask import Flask, render_template, request, jsonify
from robo_ctrl import RoboCtrl
from config_manager import load_config, save_config, update_config
import threading
import logging
import os
import sys
import shutil
import subprocess
from datetime import datetime
try:
    from serial.tools import list_ports
except Exception:
    list_ports = None

app = Flask(__name__)
app.config['JSON_AS_ASCII'] = False

# 配置日志 - 设置为 INFO 级别，显示详细日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.StreamHandler(),  # 输出到控制台
        logging.FileHandler('app.log', encoding='utf-8')  # 输出到文件
    ]
)
logger = logging.getLogger(__name__)

# 全局变量
robot = None
control_thread = None
is_running = False
_initialized = False  # 标记是否已初始化

# 加载配置
app_config = load_config()
logger.info("配置已加载: %s", app_config)

serial_port = app_config.get('comPort', 'COM3')  # 从配置文件获取串口

# Flask 启动前的初始化钩子
@app.before_request
def initialize_robot():
    """只在第一次请求时初始化机器人（避免重复初始化）"""
    global robot, _initialized, serial_port
    if not _initialized:
        _initialized = True
        logger.info("=" * 50)
        logger.info("介入手术机器人控制系统 - 首次请求初始化...")
        logger.info("=" * 50)
        logger.info(f"正在初始化机器人控制器... (端口: {serial_port})")
        try:
            robot = RoboCtrl(serial_port)
            logger.info("✓ 机器人初始化成功")
        except Exception as e:
            logger.error(f"✗ 机器人初始化失败: {e}")
        logger.info("=" * 50)

@app.route('/')
def index():
    """主页面"""
    return render_template('index.html')

@app.route('/api/push_guidewire', methods=['POST'])
def push_guidewire():
    """推送导丝"""
    try:
        data = request.json
        speed = float(data.get('speed', 10.0))
        if robot:
            robot.run_guidewire(speed)
            return jsonify({'status': 'success', 'message': '推送导丝'})
        return jsonify({'status': 'error', 'message': '机器人未连接'})
    except Exception as e:
        logger.error(f"推送导丝失败: {e}")
        return jsonify({'status': 'error', 'message': str(e)})

@app.route('/api/retract_guidewire', methods=['POST'])
def retract_guidewire():
    """撤回导丝"""
    try:
        data = request.json
        speed = float(data.get('speed', 10.0))
        if robot:
            robot.run_guidewire(-speed)  # 负速度表示撤回
            return jsonify({'status': 'success', 'message': '撤回导丝'})
        return jsonify({'status': 'error', 'message': '机器人未连接'})
    except Exception as e:
        logger.error(f"撤回导丝失败: {e}")
        return jsonify({'status': 'error', 'message': str(e)})

@app.route('/api/rotate_guidewire_cw', methods=['POST'])
def rotate_guidewire_cw():
    """顺时针旋转导丝"""
    try:
        data = request.json
        speed = float(data.get('speed', 10.0))
        if robot:
            robot.rot_guidewire(speed)
            return jsonify({'status': 'success', 'message': '顺时针旋转导丝'})
        return jsonify({'status': 'error', 'message': '机器人未连接'})
    except Exception as e:
        logger.error(f"顺时针旋转导丝失败: {e}")
        return jsonify({'status': 'error', 'message': str(e)})

@app.route('/api/rotate_guidewire_ccw', methods=['POST'])
def rotate_guidewire_ccw():
    """逆时针旋转导丝"""
    try:
        data = request.json
        speed = float(data.get('speed', 10.0))
        if robot:
            robot.rot_guidewire(-speed)  # 负速度表示逆时针
            return jsonify({'status': 'success', 'message': '逆时针旋转导丝'})
        return jsonify({'status': 'error', 'message': '机器人未连接'})
    except Exception as e:
        logger.error(f"逆时针旋转导丝失败: {e}")
        return jsonify({'status': 'error', 'message': str(e)})

@app.route('/api/push_catheter', methods=['POST'])
def push_catheter():
    """推送导管"""
    try:
        data = request.json
        speed = float(data.get('speed', 8.0))
        if robot:
            robot.run_cath(speed)
            return jsonify({'status': 'success', 'message': '推送导管'})
        return jsonify({'status': 'error', 'message': '机器人未连接'})
    except Exception as e:
        logger.error(f"推送导管失败: {e}")
        return jsonify({'status': 'error', 'message': str(e)})

@app.route('/api/retract_catheter', methods=['POST'])
def retract_catheter():
    """撤回导管"""
    try:
        data = request.json
        speed = float(data.get('speed', 8.0))
        if robot:
            robot.run_cath(-speed)  # 负速度表示撤回
            return jsonify({'status': 'success', 'message': '撤回导管'})
        return jsonify({'status': 'error', 'message': '机器人未连接'})
    except Exception as e:
        logger.error(f"撤回导管失败: {e}")
        return jsonify({'status': 'error', 'message': str(e)})

@app.route('/api/stop_all', methods=['POST'])
def stop_all():
    """停止所有动作"""
    try:
        if robot:
            robot.run_guidewire(0)
            robot.rot_guidewire(0)
            robot.run_cath(0)
            return jsonify({'status': 'success', 'message': '已停止所有动作'})
        return jsonify({'status': 'error', 'message': '机器人未连接'})
    except Exception as e:
        logger.error(f"停止动作失败: {e}")
        return jsonify({'status': 'error', 'message': str(e)})

@app.route('/api/ping', methods=['GET'])
def ping():
    """检测连接状态"""
    try:
        if robot:
            status = robot.ping()
            if status:
                return jsonify({'status': 'success', 'message': '机器人在线'})
            else:
                return jsonify({'status': 'error', 'message': '机器人无法连接'})
        return jsonify({'status': 'error', 'message': '机器人未连接'})
    except Exception as e:
        return jsonify({'status': 'error', 'message': str(e)})

@app.route('/api/update_serial_port', methods=['POST'])
def update_serial_port():
    """更新串口配置"""
    global serial_port, robot, _initialized, app_config
    try:
        data = request.json
        new_port = data.get('port', '').strip()
        
        if not new_port:
            return jsonify({'status': 'error', 'message': '端口号不能为空'})
        
        old_port = serial_port
        serial_port = new_port
        
        # 保存配置到 config.json
        update_config('comPort', new_port)
        app_config['comPort'] = new_port
        logger.info(f"串口配置已更新并保存: {old_port} -> {new_port}")
        
        # 如果机器人已连接，尝试重新连接到新端口
        if robot is not None:
            try:
                robot.close()
                logger.info(f"已关闭旧端口 {old_port}")
            except Exception as e:
                logger.warning(f"关闭旧端口时出错: {e}")
            
            try:
                robot = RoboCtrl(new_port)
                logger.info(f"✓ 已连接到新端口 {new_port}")
                return jsonify({'status': 'success', 'message': f'已成功切换到端口 {new_port}'})
            except Exception as e:
                logger.error(f"✗ 连接到新端口失败: {e}")
                robot = None
                return jsonify({'status': 'error', 'message': f'连接到新端口失败: {e}'})
        else:
            logger.info(f"串口配置已保存，将在下次初始化时使用: {new_port}")
            return jsonify({'status': 'success', 'message': f'串口配置已更新为 {new_port}'})
    except Exception as e:
        logger.error(f"更新串口配置失败: {e}")
        return jsonify({'status': 'error', 'message': str(e)})

@app.route('/api/update_video_stream', methods=['POST'])
def update_video_stream():
    """更新视频流地址"""
    global app_config
    try:
        data = request.json
        new_stream = data.get('stream', '').strip()
        
        if not new_stream:
            return jsonify({'status': 'error', 'message': '视频流地址不能为空'})
        
        old_stream = app_config.get('videoStream', '')
        
        # 保存配置到 config.json
        update_config('videoStream', new_stream)
        app_config['videoStream'] = new_stream
        logger.info(f"视频流地址已更新: {old_stream} -> {new_stream}")
        
        return jsonify({'status': 'success', 'message': f'视频流地址已更新'})
    except Exception as e:
        logger.error(f"更新视频流地址失败: {e}")
        return jsonify({'status': 'error', 'message': str(e)})

@app.route('/api/get_config', methods=['GET'])
def get_config():
    """获取当前配置"""
    global app_config
    try:
        logger.info("获取配置请求: %s", app_config)
        return jsonify({
            'status': 'success',
            'config': app_config
        })
    except Exception as e:
        logger.error("获取配置失败: %s", e)
        return jsonify({'status': 'error', 'message': str(e)})


@app.route('/api/scan_ports', methods=['GET'])
def scan_ports():
    """扫描 USB 与串口设备，返回文本输出供前端展示"""
    try:
        parts = []

        # 1) 尝试运行 lsusb（如果可用）
        if shutil.which('lsusb'):
            try:
                p = subprocess.run(['lsusb'], capture_output=True, text=True, timeout=5)
                parts.append('lsusb output:\n' + (p.stdout or p.stderr))
            except Exception as e:
                parts.append('lsusb 执行出错: %s' % e)
        else:
            parts.append('lsusb 不可用 (非 Linux 或未安装)。')

        # 2) 列出 /dev/serial/by-id 内容（仅 Linux）
        byid = '/dev/serial/by-id'
        if os.path.isdir(byid):
            try:
                items = os.listdir(byid)
                if items:
                    parts.append('/dev/serial/by-id 下的设备:\n' + '\n'.join(items))
                else:
                    parts.append('/dev/serial/by-id 目录为空')
            except Exception as e:
                parts.append('读取 /dev/serial/by-id 出错: %s' % e)
        else:
            parts.append('/dev/serial/by-id 不存在或不可访问')

        # 3) 使用 pyserial 列举串口详细信息（跨平台）
        try:
            if list_ports is not None:
                found = list_ports.comports()
                if found:
                    lines = ['发现的串口设备:']
                    for p in found:
                        lines.append('%s - %s - HWID: %s' % (p.device, p.description, p.hwid))
                    parts.append('\n'.join(lines))
                else:
                    parts.append('未发现串口设备')
            else:
                parts.append('pyserial 的 list_ports 不可用')
        except Exception as e:
            parts.append('列举串口时出错: %s' % e)

        text = '\n\n'.join(parts)
        return jsonify({'status': 'success', 'output': text})
    except Exception as e:
        logger.error('扫描端口失败: %s', e)
        return jsonify({'status': 'error', 'message': str(e)})

if __name__ == '__main__':
    # 记录启动信息
    logger.info("=" * 50)
    logger.info("介入手术机器人 Web 控制系统")
    logger.info("=" * 50)
    logger.info("Flask 应用启动中...")
    logger.info("访问地址: http://localhost:5000 或 http://0.0.0.0:5000")
    logger.info("按 CTRL+C 停止应用")
    logger.info("=" * 50)
    
    # 启动Flask应用（use_reloader=False 可以防止调试时重复初始化）
    app.run(debug=True, host='0.0.0.0', port=5000, use_reloader=False)
